﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using M3G;

/**
 * M3Gの行列演算は
 *    v' = MV
 * PostXXXはオリジナルの行列をM,引数から求めた行列をNとすると
 *    M' = MN
 * 従って後から指定した方が「先に」適応される。
 *   t.PostRotate (R1)
 *   t.PostRotate (R2)
 *   t.PostRotate (R3)
 * の時、 v' = M(R1)(R2)(R3)v
 * 要するにOpenGLと同じだが名前から想像されるのと順番が異なっていることに注意する。
 *  */

namespace MyTestProject {

    public class TestTransform {
        Transform reftra;

        public TestTransform () {
            reftra = new Transform ();
            reftra.Matrix = new float[] {1,2,0,0,
                                         2,0,1,0,
                                         1,1,1,1,
                                         0,0,2,1};

        }


        [Fact]
        public void TestConstructor1 () {
            var tra = new Transform ();
            var expected = new float[]{1,0,0,0,
                                       0,1,0,0,
                                       0,0,1,0,
                                       0,0,0,1};

            Assert.Equal (expected, tra.Matrix, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestConstructor2 () {
            var tra = new Transform (reftra);

            Assert.Equal (reftra.Matrix, tra.Matrix, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestConstructor3 () {
            var matrix = new float[]{1,2,3,4,
                                     5,6,7,8,
                                     9,10,11,12,
                                     13,14,15,16};
            var tra = new Transform (matrix);

            Assert.Equal (matrix, tra.Matrix);
        }

        /**
         * Equals系5種のテスト
         * Transformクラスに限り比較は参照の等価ではなく値の等価で行う。
         * 参考:http://msdn.microsoft.com/ja-jp/library/ms173147(v=vs.80).aspx
         * 本当はTransformをクラスではなく構造体にしたかったが、
         * C#の構造体はデフォルトコンストラクターが定義できないので
         * デフォルト単位行列にすることが出来ずあきらめた。
         * */
        [Fact]
        public void TestEquals () {
            var a = new Transform (new float[]{1,2,3,4,
                                               5,6,7,8,
                                               9,10,11,12,
                                               13,14,15,16});
            var b = new Transform (new float[]{1,2,3,4,
                                               5,6,7,8,
                                               9,10,11,12,
                                               13,14,15,16});
            Assert.True (a == b);
            Assert.False (a != b);
            Assert.True (a.Equals(b));
            Assert.True (a.Equals((object)b));
            // IEquatable<Transform>.Equals()のテストってどう書けばいいの?
        }


        [Fact]
        public void TestSetMatrix () {
            var tra = new Transform ();
            var matrix = new float[] {1,2,3,4,
                                      5,6,7,8,
                                      9,10,11,12,
                                      13,14,15,16};
            tra.Matrix = matrix;

            Assert.Equal (matrix, tra.Matrix, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestSetTransform () {
            var tra = new Transform ();
            var matrix = new float[] {1,2,3,4,
                                      5,6,7,8,
                                      9,10,11,12,
                                      13,14,15,16};
            tra.Set (reftra);

            Assert.Equal (reftra.Matrix, tra.Matrix, new FloatComparere (0.0001f));
        }


        [Fact]
        public void TestSetIdentity () {
            var tra = new Transform (reftra);
            var idt = new float[] {1,0,0,0,
                                   0,1,0,0,
                                   0,0,1,0,
                                   0,0,0,1};
            tra.SetIdentity ();

            Assert.Equal (idt, tra.Matrix, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestInvert () {
            var tra = new Transform (reftra);
            var inv = new float[]{-0.2f,  0.4f,  0.4f, -0.4f,
                                   0.6f, -0.2f, -0.2f,  0.2f,
                                   0.4f,  0.2f, -0.8f,  0.8f,
                                  -0.8f, -0.4f,  1.6f, -0.6f};
            tra.Invert ();

            Assert.Equal (inv, tra.Matrix, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestTranspose () {
            var tra = new Transform (reftra);
            var trap = new float[]{1,2,1,0,
                                   2,0,1,0,
                                   0,1,1,2,
                                   0,0,1,1};

            tra.Transpose ();

            Assert.Equal (trap, tra.Matrix, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestDeterminant () {
            var tra = new Transform (reftra);

            Assert.Equal (5.0f, tra.Determinant, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestDeterminant3x3 () {
            var tra = new Transform (reftra);

            Assert.Equal (-3.0f, tra.Determinant3x3, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestMultiply () {
            var tra = new Transform (reftra);
            tra.Multiply (2);

            var muled = new float[] {2,4,0,0,
                                     4,0,2,0,
                                     2,2,2,2,
                                     0,0,4,2};

            Assert.Equal (muled, tra.Matrix, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestAdd () {
            var tra1 = new Transform (reftra);
            var tra2 = new Transform (reftra);
            tra1.Add (tra2);

            var added = new float[] {2,4,0,0,
                                   4,0,2,0,
                                   2,2,2,2,
                                   0,0,4,2};

            Assert.Equal (added, tra1.Matrix, new FloatComparere (0.0001f));
        }



        [Fact]
        public void TestPostScale () {
            var tra = new Transform (reftra);
            tra.PostScale (1, 2, 3);

            var scaled = new float[] {1,4,0,0,
                                     2,0,3,0,
                                     1,2,3,1,
                                     0,0,6,1};
            Assert.Equal (scaled, tra.Matrix, new FloatComparere (0.0001f));
        }


        [Fact]
        public void TestPostTranslate () {
            var tra = new Transform ();
            tra.PostTranslate (1, 2, 3);

            var translated = new float[] {1,0,0,1,
                                          0,1,0,2,
                                          0,0,1,3,
                                          0,0,0,1};

            Assert.Equal (translated, tra.Matrix, new FloatComparere (0.0001f));
        }

        [Fact]
        public void TestPostRotate () {
            var tra = new Transform (reftra);
            tra.PostRotate (90, 1, 0, 0);

            var expected = new float[] {1, 0, -2, 0,
                                        2, 1, 0, 0,
                                        1, 1, -1, 1,
                                        0, 2, 0, 1 };
            Assert.Equal (expected, tra.Matrix, new FloatComparere(0.0001f));
        }

        [Fact]
        public void TestPostRotateQuat () {
            var tra = new Transform (reftra);
            tra.PostRotateQuat (1, 1, 1, 1);

            var rotq = new float[] {2,0,1,0,
                                    0,1,2,0,
                                    1,1,1,1,
                                    0,2,0,1};

            Assert.Equal (rotq, tra.Matrix, new FloatComparere(0.0001f));
        }


        [Fact]
        public void TestPostMultiply () {
            var tra1 = new Transform (reftra);
            var tra2 = new Transform (reftra);
            tra1.PostMultiply (tra2);

            var mul = new float[] {5,2,2,0,
                                   3,5,1,1,
                                   4,3,4,2,
                                   2,2,4,3};

            Assert.Equal (mul, tra1.Matrix, new FloatComparere (0.0001f));
        }

   
    }
}
